//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//Application to increase the performance of the clusters molecule characterization method implemented by Giovanni Cincilla (IRB-PCB-UB - Barcelona 2008)  //
//This method try to use the initial approximation of one matrix of data to decrease the time and number of iteration. The objetive is to use the complete //
//resource of computation, and parallel the independent task in the algorithms. This programs use the master-worker paradigm, and mpi functions.           // 
//Author: Yandi Naranjo Basalo - IRB - Barcelona - 2009                                                                                                    //
//Date of Last Change: 08-10-2009                                                                                                                          // 
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <fstream>

#include <mpi.h>

#include "ListClusteringTask.h"
#include "ListAncestorMol.h"

#include "IdxCluster.h"
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//Constanst to generate the deep of ancestor
#define NANCESTOR    4
#define NITERATION   25
#define PERCENTCONV  0.95
#define LINGOLENGTH  4
#define LENGTHREADL  10000

//Constanst to create the cluster in algorimths
#define	ARRAYSIZE    60000
#define CLUST_SIZE   1000
#define ITERATIONS   1
#define SHARED       1
#define BUFFCICLE    10000
#define LENGHTBLINE  40
#define NONZERO      0
#define MEDIAN       -0.946667
#define USE_MEDIAN   0
#define USE_DISTANCE 1
#define USE_PREFEREN 1
#define USE_MATLAB   1

#define MAXITS_1     500
#define MAXITS_2     1000 
#define CONVITS_1    50
#define CONVITS_2    100
#define DAMFACT_1    0.5
#define DAMFACT_2    0.9

//Name of APA output file 
#define APSTOPFILE   ap.stop
#define IDXOUTFILE   idx.txt
#define NETSIM2FILE  netsim2.txt
#define NETSIMFILE   netsim.txt
#define PREFFILE     pref.txt
#define SUMMARYFILE  summary.txt
#define CLUSTERRST   "clusterResult.txt"

#define FREE         0
#define BUSY         1

#define OUTSTANDING  0
#define WORKING      1
#define COMPLETE     2
#define SIZENAMEFILE 3000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int verifyAPA(char *similaritiesFolder) {

  fstream  filein;
  int      logAPA = 0;
  char    *stateFile = new char[strlen(similaritiesFolder) + 20];
  
  sprintf(stateFile, "%s/ap.stop", similaritiesFolder);
  filein.open(stateFile, ios::in);
  filein >> logAPA;
  filein.close();
  if (stateFile) delete[] stateFile;

  return logAPA;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int verifyAPAMatLab(char *similaritiesFolder) {

  fstream  filein;
  char    *line      = new char[1001];
  bool     condition = true;

  filein.open(similaritiesFolder, fstream::in);
  while (!filein.eof()) {
    filein.getline(line, 1000);
    if (strstr(line, "Warning: Algorithm did not converge")) {
        condition = false;
        break;
    }
  }
  filein.close();
  delete[] line;

  return condition;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int loadIdxAPA(char *similaritiesFolder, IdxCluster *IdxArray) {

  fstream filein;
  int     position = 0;
  char*   stateFile = new char[strlen(similaritiesFolder) + 20];

  sprintf(stateFile, "%s/idx.txt", similaritiesFolder);
  filein.open(stateFile, ios::in);
  if (stateFile) delete[] stateFile;
  
  if (!filein.is_open()) { if (stateFile) delete[] stateFile; return 0; }
  
  char* line = new char [101];
  
  while (!filein.eof())  { 
    filein.getline(line, 100);
    if (strlen(line)) {
      int idxValue = atoi(line);
      IdxArray[position++].IdxValue(idxValue);
    }
  }

  delete[] line;
  
  filein.close();
  
  return 1;

}
////---------------------------------------------------------------------------------------------------------------------------------------------------------//
////---------------------------------------------------------------------------------------------------------------------------------------------------------//
int loadIdxAPAMatLab(char *similaritiesFolder, IdxCluster *IdxArray) {

  fstream  filein;
  int      position = 0;
  char    *line     =  new char[100];

  filein.open(similaritiesFolder, fstream::in);
  if (!filein.is_open()) return 0;

  while (!filein.eof())  {
    filein.getline(line, 100);
    if (strlen(line)) {
      int idxValue = atof(line);
      IdxArray[position++].IdxValue(idxValue);
    }
  }

  filein.close();
  delete[] line;
  
  return 1;

}
////---------------------------------------------------------------------------------------------------------------------------------------------------------//
////---------------------------------------------------------------------------------------------------------------------------------------------------------//
int computeAPAMatLab(char *similaritiesFolder, char *preferenceFolder, double median, IdxCluster *IdxArray, int rank) {

  //Load the path of Matlab binary programs
  int     convergence = true;

  char*   matlabPATH  = new char[1001];
  fstream fileMatLabConf;
  int     resultSystem;
  
  fileMatLabConf.open("matlab.conf", fstream::in);
  fileMatLabConf.getline(matlabPATH, 1000);
  fileMatLabConf.close();

  //Create the temporal file with specific name for each cluster
  char *fileLSIMclusterName      = new char[100];
  char *fileLSIMclusterResult    = new char[100];
  char *fileLSIMclusterStatistic = new char[100];

  sprintf(fileLSIMclusterName,      "LSIMclusterTrial%03d.csh", rank);
  sprintf(fileLSIMclusterResult,    "LSIMclusterResult%03d",    rank);
  sprintf(fileLSIMclusterStatistic, "LSIMclusterStatistic%03d", rank);

  //Dynamic Construction of the script to execute the apcluster with Matlab
  fstream fileSIMCluster;
  fileSIMCluster.open(fileLSIMclusterName, fstream::out);

  fileSIMCluster << "unsetenv DISPLAY" << endl;
  sprintf(fileLSIMclusterName, "LSIMclusterTrial%03d", rank);
  fileSIMCluster << matlabPATH << " -r " << fileLSIMclusterName << " >&! matlab" << rank << ".log << EOF " << endl;
  fileSIMCluster << "plot(1:10)" << endl;
  fileSIMCluster << "print file" << endl;
  fileSIMCluster << "exit" << endl;
  fileSIMCluster << "EOF" << endl;
  fileSIMCluster.close();

  //Dynamic construction of Matlab code
  sprintf(fileLSIMclusterName, "LSIMclusterTrial%03d.m", rank);
  
  fstream fileSIMClusterM;
  fileSIMClusterM.open(fileLSIMclusterName, fstream::out);

  fileSIMClusterM << "load " << similaritiesFolder << endl;
  if (USE_PREFEREN) {
    fileSIMClusterM << "load " << preferenceFolder << endl;
    fileSIMClusterM << "[idx,netsim,dpsim,expref]=apcluster(" << similaritiesFolder << "," << preferenceFolder << ",'maxits'," << MAXITS_1 << ",'convits'," << CONVITS_1 << ",'dampfact'," << DAMFACT_1 << ");" << endl;
  }
  else {
    fileSIMClusterM << "[idx,netsim,dpsim,expref]=apcluster(" << similaritiesFolder << "," << median << ",'maxits'," << MAXITS_1 << ",'convits'," << CONVITS_1 << ",'dampfact'," << DAMFACT_1 << ");" << endl;
  }
  fileSIMClusterM << "save('" << fileLSIMclusterResult << "','idx','-ASCII')" << endl;
  fileSIMClusterM << "save('" << fileLSIMclusterStatistic << "','netsim','-ASCII','dpsim','-ASCII','expref','-ASCII')" << endl;
  fileSIMClusterM << "quit" << endl;
  fileSIMClusterM.close();

  sprintf(fileLSIMclusterName, "LSIMclusterTrial%03d.csh", rank);

  //Execution of Script
  char *command = new char[1000];
  sprintf(command, "csh %s", fileLSIMclusterName); 
  resultSystem = system(command);

  //Verification of the convergence process
  char *convergenceFile = new char[1000];
  sprintf(convergenceFile, "matlab%d.log", rank);
  convergence = verifyAPAMatLab(convergenceFile);
  delete[] convergenceFile;

  if (!convergence) {
      
    sprintf(command, "rm LSIMclusterResult%03d",    rank); 
    resultSystem = system(command);
    sprintf(command, "rm LSIMclusterStatistic%03d", rank); 
    resultSystem = system(command);

    //Dynamic construction of matlab code
    sprintf(fileLSIMclusterName, "LSIMclusterTrial%03d.m", rank);

    fileSIMClusterM.open(fileLSIMclusterName, fstream::out);

    fileSIMClusterM << "load " << similaritiesFolder << endl;
    if (USE_PREFEREN) {
      fileSIMClusterM << "load " << preferenceFolder << endl;
      fileSIMClusterM << "[idx,netsim,dpsim,expref]=apcluster(" << similaritiesFolder << "," << preferenceFolder << ",'maxits'," << MAXITS_2 << ",'convits'," << CONVITS_2 << ",'dampfact'," << DAMFACT_2 << ");" << endl;
    }
    else {
      fileSIMClusterM << "[idx,netsim,dpsim,expref]=apcluster(" << similaritiesFolder << "," << median << ",'maxits'," << MAXITS_2 << ",'convits'," << CONVITS_2 << ",'dampfact'," << DAMFACT_2 << ");" << endl;
    }
    fileSIMClusterM << "save('" << fileLSIMclusterResult << "','idx','-ASCII')" << endl;
    fileSIMClusterM << "save('" << fileLSIMclusterStatistic << "','netsim','-ASCII','dpsim','-ASCII','expref','-ASCII')" << endl;
    fileSIMClusterM << "quit" << endl;
    fileSIMClusterM.close();

    sprintf(fileLSIMclusterName, "LSIMclusterTrial%03d.csh", rank);

    //Execution of Script
    sprintf(command, "csh %s", fileLSIMclusterName); 
    resultSystem = system(command);
    
  }

  loadIdxAPAMatLab(fileLSIMclusterResult, IdxArray);

  sprintf(command, "rm matlab%d.log",             rank); 
  resultSystem = system(command);
  sprintf(command, "rm LSIMclusterTrial%03d.csh", rank); 
  resultSystem = system(command);
  sprintf(command, "rm LSIMclusterTrial%03d.m",   rank); 
  resultSystem = system(command);
  sprintf(command, "rm LSIMclusterResult%03d",    rank); 
  resultSystem = system(command);
  sprintf(command, "rm LSIMclusterStatistic%03d", rank); 
  resultSystem = system(command);

  delete[] fileLSIMclusterName;
  delete[] fileLSIMclusterResult;
  delete[] fileLSIMclusterStatistic;
  delete[] matlabPATH;
  delete[] command;

  return 1;

}
////---------------------------------------------------------------------------------------------------------------------------------------------------------//
////---------------------------------------------------------------------------------------------------------------------------------------------------------//
int computeAPA(char *similaritiesFolder, char *preferenceFolder, double median, IdxCluster *IdxArray, int length) {

  int firstExec   = true, iterations  = true, convergence = true, resultSystem;

  char *folder = new char[strlen(similaritiesFolder) + 1];
  strcpy(folder, similaritiesFolder);
  folder[strlen(folder) - 4] = '\0';

  while (iterations) {
    char *command = new char[1000];
    if (USE_PREFEREN) {
      if (firstExec) sprintf(command, "./apcluster %s %s %s maxits=%d convits=%d dampfact=%f", similaritiesFolder, preferenceFolder, folder, MAXITS_1, CONVITS_1, DAMFACT_1);
      else           sprintf(command, "./apcluster %s %s %s maxits=%d convits=%d dampfact=%f", similaritiesFolder, preferenceFolder, folder, MAXITS_2, CONVITS_2, DAMFACT_2);
    }
    else {
      if (USE_MEDIAN) {
        if (firstExec) sprintf(command, "./apcluster %s %f %s maxits=%d convits=%d dampfact=%f", similaritiesFolder, MEDIAN, folder, MAXITS_1, CONVITS_1, DAMFACT_1);
        else           sprintf(command, "./apcluster %s %f %s maxits=%d convits=%d dampfact=%f", similaritiesFolder, MEDIAN, folder, MAXITS_2, CONVITS_2, DAMFACT_2);
      }
      else {
        if (firstExec) sprintf(command, "./apcluster %s %f %s maxits=%d convits=%d dampfact=%f", similaritiesFolder, median, folder, MAXITS_1, CONVITS_1, DAMFACT_1);
        else           sprintf(command, "./apcluster %s %f %s maxits=%d convits=%d dampfact=%f", similaritiesFolder, median, folder, MAXITS_2, CONVITS_2, DAMFACT_2);
      }
    }
    cout << command << endl;
    resultSystem = system(command);
    if (command) delete[] command;

    convergence = verifyAPA(folder);
    if ((!convergence) || (convergence && !firstExec)) break;
    firstExec = false;
  }

  if (!convergence)
    loadIdxAPA(folder, IdxArray);
  else
    for (int i = 0; i < length; i++)
      IdxArray[i].IdxValue(i + 1);

  char *command = new char[1000];
  sprintf(command, "rm %s/*.*;rmdir %s", folder, folder);
  resultSystem = system(command);
  if (command) delete[] command;
  if (folder) delete[] folder;

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int clusteringProcess(MatrixTool* matrixClustering, int rank, char *resultFile) {

  //Similarity File
  fstream fileResult;
  sprintf(resultFile, "tmp_Result%05d.txt", rank);
  fileResult.open(resultFile, fstream::in | fstream::out | fstream::app);

  //Similarity File
  fstream fileout;
  char*   similaritiesFile = new char[SIZENAMEFILE];
  char*   preferenceFile   = new char[SIZENAMEFILE];
  int     resultSystem;
  
  sprintf(similaritiesFile, "Similarities%05d.txt", rank);
  sprintf(preferenceFile,   "Preference%05d.txt",   rank);
  
  fileout.open(similaritiesFile, ios::out);

  char* buffer     = NULL;
  char* bufferLine = new char[LENGHTBLINE + 1];

  //Preference File
  if (USE_PREFEREN) {
    fstream fileoutP;
    fileoutP.open(preferenceFile, ios::out);
    for (int i = 0; i < matrixClustering->Labels()->Length(); i++) 
      fileoutP << ((!i)?"":"\n") << MEDIAN; 
    fileoutP.close();
  }

  buffer    = new char[LENGHTBLINE*BUFFCICLE + 1];
  buffer[0] = '\0';

  int     counterCicles = 0;
  double  medianValue   = 0;

  //Compute the LingoSim for the group of smiles in DB
  for (int i = 0; i < matrixClustering->Labels()->Length(); i++) 
    for (int j = 0; j < i; j++) {

      double similarity = matrixClustering->Matrix()[i][j];

      if (USE_DISTANCE) similarity = similarity - 1;

      medianValue += similarity;

      sprintf(bufferLine, "%04d  %04d  %.6f\n",  i + 1,      j + 1, similarity);
      sprintf(buffer,     "%s%s",               buffer, bufferLine);

      sprintf(bufferLine, "%04d  %04d  %.6f\n",  j + 1,      i + 1, similarity);
      sprintf(buffer,     "%s%s",               buffer, bufferLine);

      counterCicles += 2;

      if (counterCicles >= BUFFCICLE) {
        counterCicles = 0;
        fileout << buffer;
        buffer[0] = '\0';
      }

    }

  medianValue *= 2;
  medianValue += (USE_DISTANCE)?0:matrixClustering->Labels()->Length();
  medianValue /= (matrixClustering->Labels()->Length() * matrixClustering->Labels()->Length());

  if (counterCicles < BUFFCICLE) {
    counterCicles = 0;
    fileout << buffer;
    buffer[0] = '\0';
  }

  if (buffer) delete[] buffer;

  fileout.close();

  //Compute the clusters with APA
  IdxCluster *IdxArray = new IdxCluster[matrixClustering->Labels()->Length()];
  for (int i = 0; i < matrixClustering->Labels()->Length(); i++) 
    IdxArray[i].IdxValue(-1);

  computeAPA(similaritiesFile, preferenceFile, medianValue, IdxArray, matrixClustering->Labels()->Length());

  //Load the exemplars and store the trace
  for (int i = 0; i < matrixClustering->Labels()->Length(); i++) {
    fileResult << left;
    fileResult.width(12);
    fileResult << matrixClustering->Labels()->Get(i)->Out() << "  " << matrixClustering->Labels()->Get(IdxArray[i].IdxValue() - 1)->Out() << endl;
  }

  char *cmdDelFile = new char[strlen(similaritiesFile) + 10];
  
  sprintf(cmdDelFile, "rm %s", similaritiesFile);
  resultSystem = system(cmdDelFile);
  
  if (USE_PREFEREN) {
    sprintf(cmdDelFile, "rm %s", preferenceFile);
    resultSystem = system(cmdDelFile);
  }
  
  if (similaritiesFile) delete[] similaritiesFile;
  if (preferenceFile) delete[] preferenceFile;

  if (IdxArray) delete[] IdxArray;

  fileResult.close();

  return 1;

}
////---------------------------------------------------------------------------------------------------------------------------------------------------------//
////---------------------------------------------------------------------------------------------------------------------------------------------------------//
int clusteringProcessMatLab(MatrixTool* matrixClustering, int rank, char *resultFile) {

  //Similarity File
  fstream fileResult;
  sprintf(resultFile, "tmp_Result%05d.txt", rank);
  fileResult.open(resultFile, fstream::in | fstream::out | fstream::app);

  //Similarity File
  fstream fileout;
  char*   similaritiesFile = new char[SIZENAMEFILE];
  char*   preferenceFile   = new char[SIZENAMEFILE];
  
  sprintf(similaritiesFile, "Similarities%05d", rank);
  sprintf(preferenceFile,   "Preference%05d",   rank);
  
  fileout.open(similaritiesFile, ios::out);

  char *buffer     = NULL;
  char *bufferLine = new char[LENGHTBLINE + 1];

  //Preference File
  if (USE_PREFEREN) {
    fstream fileoutP;
    fileoutP.open(preferenceFile, fstream::out);
    for (int i = 0; i < matrixClustering->Labels()->Length(); i++) 
      fileoutP << ((!i)?"":"\n") << MEDIAN; 
    fileoutP.close();
  }

  buffer    = new char[LENGHTBLINE * BUFFCICLE + 1];
  buffer[0] = '\0';

  int     counterCicles = 0;
  double  medianValue   = 0;

  //Put the value of Similarities in File
  for (int i = 0; i < matrixClustering->Labels()->Length(); i++)
    for (int j = 0; j < i; j++) {

      double similarity = matrixClustering->Matrix()[i][j];

      if (USE_DISTANCE) similarity = similarity - 1;

      medianValue += similarity;

      sprintf(bufferLine, "%04d  %04d  %.6f\n",  i + 1,      j + 1, similarity);
      sprintf(buffer,     "%s%s",               buffer, bufferLine);

      sprintf(bufferLine, "%04d  %04d  %.6f\n", j + 1,       i + 1, similarity);
      sprintf(buffer,     "%s%s",               buffer, bufferLine);

      counterCicles += 2;

      if (counterCicles >= BUFFCICLE) {
        counterCicles = 0;
        fileout << buffer;
        buffer[0] = '\0';
      }

    }

  medianValue *= 2;
  medianValue += (USE_DISTANCE)?0:matrixClustering->Labels()->Length();
  medianValue /= (matrixClustering->Labels()->Length() * matrixClustering->Labels()->Length());

  if (counterCicles < BUFFCICLE) {
    counterCicles = 0;
    fileout << buffer;
    buffer[0] = '\0';
  }

  if (buffer) delete[] buffer;
  fileout.close();

  //Compute the clusters with the APA
  IdxCluster *IdxArray = new IdxCluster[matrixClustering->Labels()->Length()];
  for (int i = 0; i < matrixClustering->Labels()->Length(); i++) 
    IdxArray[i].IdxValue(-1);

  computeAPAMatLab(similaritiesFile, preferenceFile, medianValue, IdxArray, rank);

  //Load the exemplars and store the trace
  for (int i = 0; i < matrixClustering->Labels()->Length(); i++) {
    if (IdxArray[i].IdxValue() == -1) continue;
    fileResult << left;
    fileResult.width(12);
    fileResult << matrixClustering->Labels()->Get(i)->Out() << "  " << matrixClustering->Labels()->Get(IdxArray[i].IdxValue() - 1)->Out();
    if (i < (matrixClustering->Labels()->Length() - 1)) fileResult << endl;
  }

  char* cmdDelFile = new char[strlen(similaritiesFile) + 10];
  int   resultSystem;
  
  sprintf(cmdDelFile, "rm %s", similaritiesFile);
  resultSystem = system(cmdDelFile);
  
  if (USE_PREFEREN) {
    sprintf(cmdDelFile, "rm %s", preferenceFile);
    resultSystem = system(cmdDelFile);
  }

  if (similaritiesFile) delete[] similaritiesFile;
  if (preferenceFile) delete[] preferenceFile;

  if (IdxArray) delete[] IdxArray;

  fileResult.close();

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int splitLineIdCode(char *line, int *idCodes, int size) {

  char *pch, *lineTmp = new char[strlen(line) + 1];
  int   count = 0;
  
  strcpy(lineTmp, line);

  pch              = strtok(lineTmp, " ");
  idCodes[count++] = atoi(pch);

  while (pch != NULL) {
    pch              = strtok(NULL, " ");
    if (count < size) idCodes[count++] = atoi(pch);
  }

  delete[] lineTmp;

  return 1;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int splitPosLineIdCode(char *line, int pos) {

  char *pch, *lineTmp = new char[strlen(line) + 1];
  int   count = 1;

  strcpy(lineTmp, line);

  pch = strtok(lineTmp, " ");
  if (pos == 1) return atoi(pch);

  while (pch != NULL) {
    pch = strtok(NULL, " ");
    if (++count == pos) return atoi(pch);
  }

  delete[] lineTmp;

  return -1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int prepareNewDB(char *dataFile, char *outFile, int totalNumberSmiles/*, smileStore *molDB*/) {
//
//  fstream fileResult;
//  fileResult.open(dataFile, fstream::in);
//  fileResult.seekg (0, fstream::beg);
//
//  int *codeId = new int[totalNumberSmiles];
//
//  molDB->Filename(outFile);
//  molDB->ExtractSmiles();
//
//  int   count = 0;
//  char *line = new char[1000];
//  while (!fileResult.eof()) {
//    fileResult.getline(line, 1000);
//    if (!strlen(line)) continue;
//    codeId[count++] = splitPosLineIdCode(line, 2);
//  }
//  if (line) delete[] line;
//
//  smileStruct::MergeSort(codeId, totalNumberSmiles);
//  totalNumberSmiles = smileStruct::CompactList(codeId, totalNumberSmiles);
//  fileResult.close();
//
//  fstream newFile;
//  sprintf(outFile, "DB_Molecule_TMP.tmp\0");
//  newFile.open(outFile, fstream::out);
//
//  for (int i = 0; i < totalNumberSmiles; i++)
//    for (int j = 0; j < molDB->NumberSmiles(); j++)
//      if (atoi(molDB->OneSmileStruct(j)->Id()) == codeId[i]) {
//          newFile << molDB->OneSmileStruct(j)->Code() << " " << molDB->OneSmileStruct(j)->Id() << endl;
//          break;
//      }
//
//  newFile.close();
//
//  if (codeId) delete[] codeId;
//
  return 1;
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int loadMoleculeCluster(char *filename, long **&listClusterFile, int numberMolecules) {

  if (!filename) return 0;
  
  fstream fileInput;
  fileInput.open(filename, fstream::in);

  int   count = 0;
  
  while (!fileInput.eof()) {
    char *line  = new char[1001];
    fileInput.getline(line, 1000);
    if (strlen(line)) {
      listClusterFile[count][0]   = splitPosLineIdCode(line, 1);
      listClusterFile[count++][1] = splitPosLineIdCode(line, 2);
    }
    if (count > numberMolecules) break;
    delete[] line;
  }
  
  
  fileInput.close();

  return 1;
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int findCommonData(long *list1, long *list2, int nlist1, int nlist2) {

  int numberCommonData = 0;
  for (int i = 0; i < nlist1; i++) for (int j = 0; j < nlist2; j++) if (list1[i] == list2[j]) { numberCommonData++; break; }
  return numberCommonData;
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double computeConvergence(long **&listClusterFileOld, long **&listClusterFileNew, int numberMolecules) {

  long   *listNew = new long[numberMolecules];
  long   *listOld = new long[numberMolecules];
  
  double convergenceValue = 0;
  double factor, fnumberCommonCluster, fnumberOldCluster, fnumberNewCluster;

  for (int i = 0; i < numberMolecules; i++) {
      
    //Initializate the subList of Molecule
    for (int j = 0; j < numberMolecules; j++) listOld[j] = listNew[j] = -1;
    
    //Get the father fot the New Cluster of Molecules
    int numberNewCluster = 0, numberOldCluster = 0, numberCommonCluster = 0;
    
    for (int j = 0; j < numberMolecules; j++) 
      if (listClusterFileNew[i][1] == listClusterFileNew[j][1]) 
        listNew[numberNewCluster++] = listClusterFileNew[j][0];
    
    long fatherMolOld = -1;
    
    for (int j = 0; j < numberMolecules; j++)
      if (listClusterFileNew[i][0] == listClusterFileOld[j][0]) {
        fatherMolOld = listClusterFileOld[j][1];
        break;
      }
    
    for (int j = 0; j < numberMolecules; j++)
      if (fatherMolOld == listClusterFileOld[j][1])
        listOld[numberOldCluster++] = listClusterFileOld[j][0];

    if (numberOldCluster < numberNewCluster) numberCommonCluster = findCommonData(listOld, listNew, numberOldCluster, numberNewCluster);
    else                                     numberCommonCluster = findCommonData(listNew, listOld, numberNewCluster, numberOldCluster);

    fnumberCommonCluster = numberCommonCluster; fnumberOldCluster = numberOldCluster; fnumberNewCluster = numberNewCluster;
    factor = (fnumberCommonCluster - 1)/(fnumberOldCluster + fnumberNewCluster - fnumberCommonCluster);
    convergenceValue += factor;

  }

  delete[] listNew;
  delete[] listOld;

  return convergenceValue;
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool analizeConvergence(int iteration, int nSmiles, int rank, double &convergenceValue) {

  int   resultSystem;
  
  if (!iteration) {
    if (!rank) {
      char* command = new char[100];
      sprintf(command, "cp Cluster_Molecules.txt Cluster_Molecules%03d.txt", iteration); 
      resultSystem = system(command);
      sprintf(command, "rm Cluster_Molecules.txt"); 
      resultSystem = system(command);
      if (command) delete[] command;
    }
    return false;
  }

  if (!rank) {

    long **listClusterFileOld = new long*[nSmiles];
    long **listClusterFileNew = new long*[nSmiles];

    for (int i = 0; i < nSmiles; i++) {
      listClusterFileOld[i] = new long[2];
      listClusterFileNew[i] = new long[2];
    }

    char *command = new char[100];
    sprintf(command, "cp Cluster_Molecules.txt Cluster_Molecules%03d.txt", iteration); 
    resultSystem = system(command);
    sprintf(command, "rm Cluster_Molecules.txt"); 
    resultSystem = system(command);

    sprintf(command, "Cluster_Molecules%03d.txt", iteration - 1);
    loadMoleculeCluster(command, listClusterFileOld, nSmiles);

    sprintf(command, "Cluster_Molecules%03d.txt", iteration);
    loadMoleculeCluster(command, listClusterFileNew, nSmiles);

    if (command) delete[] command;

    double valueConvergence = computeConvergence(listClusterFileOld, listClusterFileNew, nSmiles);
    double ownConvergence   = computeConvergence(listClusterFileNew, listClusterFileNew, nSmiles);

    for (int i = 0; i < nSmiles; i++) {
      if (listClusterFileOld[i]) delete[] listClusterFileOld[i];
      if (listClusterFileNew[i]) delete[] listClusterFileNew[i];
    }
    if (listClusterFileOld) delete[] listClusterFileOld;
    if (listClusterFileNew) delete[] listClusterFileNew;

    double percentConvergence = PERCENTCONV;
    convergenceValue = valueConvergence/ownConvergence;
    if (convergenceValue >= percentConvergence) return true;
    return false;

  }
  return false;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int main (int argc, char *argv[]) {

  int                 err, rank, sizeRank;
  //MPI_Request	        reqs[ARRAYSIZE];	  
  MPI_Status	        Stat;               

  char 	              filename[LENGTHREADL + 1];
  
  bool                convergence      = false;
  int                 nconvergence     = 0;
  
  MatrixTool*         matrixClustering = NULL;

  ListRankStatus*     rankStatus       = NULL;
  ListClusteringTask* clusteringTasks  = NULL;
  ListAncestorMol*    listAncestor     = NULL;
  
  TListE <String>*    labelsClustering = NULL;
  
  err = MPI_Init(&argc, &argv);
  
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &sizeRank);
  
  if (!rank) {
    
    if      (argc < 2)  { printf("What's the name of input file? \t"); cin >> filename; }
    else if (argc == 2) { strcpy(filename, argv[1]); }

    //Variable that contain the original Matrix 
    matrixClustering = new MatrixTool (filename);
    matrixClustering->ReadFile(); 

    //List of node involved in the work
    rankStatus       = new ListRankStatus (sizeRank, 0);
    listAncestor     = new ListAncestorMol ();

    listAncestor->MatrixSimilarity(matrixClustering);
    listAncestor->SetListAncestor(matrixClustering->Labels());

    clusteringTasks  = new ListClusteringTask ();
    clusteringTasks->ListRanks(rankStatus);
    
    labelsClustering = new TListE <String> ();

    //Iterate until converge Clustering Process
    while (!convergence) {

      //Clustering in Dependence of Ancestor Level
      for (int ancestor = 0; ancestor < NANCESTOR; ancestor++) {

        if (nconvergence && !ancestor) {             
          
          listAncestor->OrderListByAncestorCenteredMatrixIterative();
          
          char filenameAncestor[SIZENAMEFILE];
          sprintf(filenameAncestor, "%s.Cluster.Ancestors.%05d.dat", filename, nconvergence);
          listAncestor->FilenameAncestor(filenameAncestor);
          listAncestor->WriteAncestorFile();
          
        }

        listAncestor->GetAncestorByLevel(labelsClustering, ancestor);
        clusteringTasks->CreateListTaskWithoutAssign(labelsClustering, CLUST_SIZE);
cout << "1" << endl;
        cout << endl << "Ancestor: " << ancestor << endl;
        cout << "Size: " << labelsClustering->Length() << endl;
        long amount = 0;
        labelsClustering->SetInitial();
        while (labelsClustering->SetNext()) 
          cout << ++amount << " - " << labelsClustering->GetCurrent()->Out() << endl;
cout << "2" << endl;
        fstream fileClusterResult;
        String  clusterResultFilename = CLUSTERRST;
        
        fileClusterResult.open(clusterResultFilename.Out(), fstream::in | fstream::out | fstream::app);
        
        long  lengthRecv = 0;
        char* bufferRecv = NULL;

        //Repeat while exists any outstanding job or working job 
        while (clusteringTasks->NextTask(OUTSTANDING) || clusteringTasks->NextTask(WORKING)) {

          ClusteringTask* currentTask    = NULL;
          RankStatus*     currentJobRank = NULL;

          //Send job while exist one outstanding job and one free node
          while (((currentTask    = clusteringTasks->NextTask(OUTSTANDING))                       != NULL) && 
                 ((currentJobRank = clusteringTasks->ListRanks()->NextFreeRankExcludeFirst(FREE)) != NULL)) {

            currentTask->Rank(currentJobRank->Rank());
            TListE <String>* subList           = labelsClustering->GetSubList(currentTask->Initial(), currentTask->End());
            char*            subMatrixText     = matrixClustering->GetSubMatrix(currentTask->Initial(), currentTask->End());
            char*            subListText       = subList->ToString();
            long             sizeSubMatrixText = strlen(subMatrixText);
            long             sizeSubListText   = strlen(subListText);

            MPI_Send(&sizeSubListText, 1, MPI_LONG, currentTask->Rank(), (10 + currentTask->Rank()), MPI_COMM_WORLD);
            
            if (sizeSubListText) {
              MPI_Send(       subListText,   sizeSubListText, MPI_CHAR, currentTask->Rank(), (20 + currentTask->Rank()), MPI_COMM_WORLD);
              MPI_Send(&sizeSubMatrixText,                 1, MPI_LONG, currentTask->Rank(), (30 + currentTask->Rank()), MPI_COMM_WORLD);
              MPI_Send(     subMatrixText, sizeSubMatrixText, MPI_CHAR, currentTask->Rank(), (40 + currentTask->Rank()), MPI_COMM_WORLD);
            }

            currentTask->Status(WORKING);
            currentJobRank->Status(BUSY);
            
            if (subMatrixText) delete[] subMatrixText;
            if (subListText)   delete[] subListText;

          }
          
          //Join the result in the listAncestor
          if (bufferRecv) {
            //cout << bufferRecv << endl;
            fileClusterResult << bufferRecv << endl;
            delete[] bufferRecv;
          }
          
          if (clusteringTasks->NextTask(WORKING)) {
            
            //Receive response from any worker with cluster result
            MPI_Recv(&lengthRecv, 1, MPI_LONG, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &Stat);
            
            if (lengthRecv) {
              
              int   sourceRank = Stat.MPI_SOURCE;
              int   sourceTag  = Stat.MPI_TAG;

              bufferRecv = new char[lengthRecv + 1];
              MPI_Recv(bufferRecv, lengthRecv, MPI_CHAR, sourceRank, sourceTag + 10, MPI_COMM_WORLD, &Stat);
              bufferRecv[lengthRecv] = '\0';

              currentTask    = clusteringTasks->GetTaskByRank(sourceRank);
              currentJobRank = clusteringTasks->ListRanks()->GetRank(sourceRank);

              if (currentTask)    currentTask->Status(COMPLETE);
              if (currentJobRank) currentJobRank->Status(FREE);

            }
            
          }

        }
        
        //Join the result in the listAncestor
        if (bufferRecv) {
          fileClusterResult << bufferRecv;
          delete[] bufferRecv;
        }
        
        fileClusterResult.close();
        
        cout << "Step: " << ancestor << " Iteration: " << nconvergence + 1 << endl;
        
        listAncestor->AddAncestorFromFile(&clusterResultFilename, ancestor);

/*        char command[SIZENAMEFILE];
        sprintf(command, "rm %s", clusterResultFilename.Out());
        system(command);
*/        
      }
      
      long test;
      cin >> test;
      
      /*  
      if (!rank) {

        char *command = new char[100];
        sprintf(command, "rm %s\0", filename);
        system(command);clusterResultFilename
        if (command) delete[] command;

      }

      //Analysis the convergence with the previous iteration
      double convergenceValue = 0;
      convergence = analizeConvergence(nconvergence, nSmiles, rank, convergenceValue);
      */
      nconvergence++;

    }
    
  }
  //Worker Process
  else {
    
    char* subMatrixText     = NULL;
    char* subListText       = NULL;
    long  sizeSubMatrixText;
    long  sizeSubListText;

    do {

      MPI_Recv(&sizeSubListText, 1, MPI_LONG, 0, (10 + rank), MPI_COMM_WORLD, &Stat);

      if (sizeSubListText) {

        subListText = new char[sizeSubListText + 1];
        MPI_Recv(subListText, sizeSubListText, MPI_CHAR, 0, (20 + rank), MPI_COMM_WORLD, &Stat);
        subListText[sizeSubListText] = '\0';
        MPI_Recv(&sizeSubMatrixText, 1, MPI_LONG, 0, (30 + rank), MPI_COMM_WORLD, &Stat);

        subMatrixText = new char[sizeSubMatrixText + 1];
        MPI_Recv(subMatrixText, sizeSubMatrixText, MPI_CHAR, 0, (40 + rank), MPI_COMM_WORLD, &Stat);
        subMatrixText[sizeSubMatrixText] = '\0';

        int   stateWork;
        char* resultFile = new char[SIZENAMEFILE];

        labelsClustering = new TListE <String> ();
        matrixClustering = new MatrixTool ();

        matrixClustering->SplitColumn(subListText, labelsClustering, "\n");
        matrixClustering->ReadFromString(labelsClustering, subMatrixText);

        if (subListText)   delete[] subListText;
        if (subMatrixText) delete[] subMatrixText;

        if (USE_MATLAB) stateWork = clusteringProcessMatLab(matrixClustering, rank, resultFile);
        else            stateWork = clusteringProcess(matrixClustering, rank, resultFile);

        if (stateWork) {
          
          fstream fileResult;
          sprintf(resultFile, "tmp_Result%05d.txt", rank);
          fileResult.open(resultFile, fstream::in);

          fileResult.seekg (0, fstream::end);
          long length = fileResult.tellg();
          fileResult.seekg (0, fstream::beg);
          char *buffer = new char[length + 1];

          fileResult.read(buffer, length);
          fileResult.close();
          
          buffer[length] = '\0';
          
          MPI_Send(&length,      1, MPI_LONG, 0, (50 + rank), MPI_COMM_WORLD);
          MPI_Send(buffer , length, MPI_CHAR, 0, (60 + rank), MPI_COMM_WORLD);

          if (buffer) delete[] buffer;

          char* command = new char[SIZENAMEFILE];
          int   resultSystem;
          
          sprintf(command, "rm %s", resultFile); 
          resultSystem = system(command);
          
          if (command) delete[] command;
            
        }
        else {
          long length = 0;
          MPI_Send(&length, 1, MPI_LONG, 0, (50 + rank), MPI_COMM_WORLD);
        }

        if (resultFile) delete[] resultFile;
        
      }

      matrixClustering->Labels()->TransferAll();

      if (labelsClustering) delete labelsClustering;
      if (matrixClustering) delete matrixClustering;
        
    } while (sizeSubListText);
    
  }
  
  MPI_Finalize();

  return EXIT_SUCCESS;
 	
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
